參加 IT 鐵人賽目前已經進入第七天了,發現其實產文章不是最難的,反而每天都必須撥時間在電腦前才是最困難的,我都沒辦法不帶電腦出遠門了QQ。既然如此,那我們就寫個排程讓系統自己每天發文章,我只需要把文章準備好就好了。
IT 鐵人賽要發一篇文章的流程其實不困難,大致流程就四個動作
而在這些動作流程跑下來,發現在第二步驟「點選主題」時產生了一個 create 的 request,然後我們被導向一個帶有類似 article id 的頁面
接著在點選第四步驟「發表文章」時,發現他又送了一個關鍵的 publish request 出去,裡頭帶著文章標題、文章內容、tags,還有兩個神秘的參數 _token 和 _method,接著就發文成功了,可見我們只要搞定這兩個 request,那麼應該就大功告成了。
ps. 這兩個神秘參數對網站來說有其意義,所以並不能省略,一個是發文的 token 驗證,一個是 request 的 method,因為 browser 其實無法容易的送出 put 和 delete method。
但在繼續之前,我們忘記了一件事情,就是肯定要先登入才能夠發文呀。在 Day 4 爬蟲原則和技巧中有提到,對於 http 來說,每次的 request 都是 stateless,也就是說,在這個 request 裡面肯定有某樣東西是驗證我們為已登入狀態。易地而處的思考一下,若我們是網頁工程師,那麼若要驗證使用者,肯定第一個會想到的就是 session 和 cookie。
所以直覺的就來測試看看把 cookie 加上去,看起來是可以確保是登入狀態,同時我們檢查一下 cookie 的有效期限有 30d,看起來挺夠的。
接下來我們模擬 create 文章的 request,同時帶上 cookie,然後我們得到一個帶有 article id 的 302 redirect response。
接著我們對這個 article id 做 publish request,一樣帶上 cookie 還有事先準備好的文章內容外加那兩個神秘的參數 _token 和 _method,送出後我們就看到成功的 response,搞定!
先把準備好的 cookie、post subject、post description、tags 拿起來備用。
// 請自行使用自己的 cookie,小心不要放在公開的地方…
var cookie = 'xxxxxx';
var postContent = {
subject: '預先準備好的標題',
description: '預先準備好的內容',
tags: ['test1', 'test2'],
};
接下來實作 createPost function,這個 get request 不用帶任何參數,只要一個固定的網址(網址每個人都不一樣,請看自己的建立主題文章的網址),不過在 options 裡面我加上 followRedirect: false
,因為這個 request response 會直接 302 redirect,為了比較好抓 response 裡面的 article ID,所以我停止自動 redirect,然後我用 regex 將抓取到的 article ID 丟給 callback。
ps. 在使用 regex expression 的時候,我常用 https://regex101.com/ 來做測試
function createPost(callback) {
var options = {
url: 'https://ithelp.ithome.com.tw/articles/create?group=tech',
followRedirect: false,
headers: {
'cookie': cookie
}
};
request(options, (err, res, body) => {
var articleId = body.match(/articles\/(.+)\/draft/)[1];
callback(articleId);
})
}
然後我們就準備來發文章了,發文章只需要一個 post request,這裡 post 過去的 url 會包含剛剛得到的 article ID,同時帶上觀察到的 form 的參數,然後再放個 callback 驗證一下 request 完成。
function publishPost(articleId, postContent, callback) {
var options = {
url: `https://ithelp.ithome.com.tw/articles/${articleId}/publish`,
followRedirect: false,
method: 'POST',
headers: {
'Cookie': cookie,
'Content-Type': 'application/x-www-form-urlencoded',
},
form: {
// 請自行觀察 token 內容
'_token': 'xxxxxx',
'group': 'tech',
'_method': 'PUT',
'subject': postContent.subject,
'description': postContent.description,
'tags': postContent.tags,
},
};
request(options, (err, res, body) => {
callback()
})
}
我們完成了所有元件,接下來就來組合流程吧,首先先 create post,取得 article ID 之後,就丟入給 publish function。輕鬆寫意,不過在測試的時候記得去刪掉你發表出去的測試文章!
createPost( articleId => {
publishPost(articleId, postContent, () => {
console.log('完成發文');
})
})
const request = require('request');
const cheerio = require('cheerio');
// 請自行使用自己的 cookie,小心不要放在公開的地方…
var cookie = 'xxxxxx';
var postContent = {
subject: '預先準備好的標題',
description: '預先準備好的內容',
tags: ['test1', 'test2'],
};
createPost(articleId => {
publishPost(articleId, postContent, () => {
console.log('完成發文');
})
})
function createPost(callback) {
var options = {
url: 'https://ithelp.ithome.com.tw/articles/create?group=tech',
followRedirect: false,
headers: {
'cookie': cookie
}
};
request(options, (err, res, body) => {
var articleId = body.match(/articles\/(.+)\/draft/)[1];
callback(articleId);
})
}
function publishPost(articleId, postContent, callback) {
var options = {
url: `https://ithelp.ithome.com.tw/articles/${articleId}/publish`,
method: 'POST',
headers: {
'Cookie': cookie,
'Content-Type': 'application/x-www-form-urlencoded',
},
form: {
// 請自行觀察 token 內容
'_token': 'xxxxxx',
'group': 'tech',
'_method': 'PUT',
'subject': postContent.subject,
'description': postContent.description,
'tags': postContent.tags,
},
};
request(options, (err, res, body) => {
callback()
})
}
當我們可以輕鬆的執行發文之後,我們還需要一個排程工具來幫助我們在固定時間發文。想到排程就肯定是 crontab 當能不讓了,可以將預先寫好的文章內容,用檔名分好檔案,然後排程讓 crontab 在當天就讀取該檔案的內容發送文章,如此一來,我們就只要專注在產生文章的檔案就行了。
ps. cookie 有有效期限,並且隨時可能會被 disable,所以儘可能的話,在發表文章之後,可以送一個 notifiy 到我們的 email,這樣才能萬無一失。
啊...是 Amos 的徒弟呀,昨天才聽 Amos 提到你
前輩怎知!!我只知道前輩是好想工作室的人!!
(但我當初是因為想學爬蟲才追蹤的XDDDD
叫前輩太老了...
我有去看你去年參賽文,最後一篇有提到 Amos,而昨天跟 Amos 吃薑補鴨他也有提到,才聯想起來的
哈哈~昨天和師傅去吃薑母鴨XD 師傅有說我什麼嗎
好久不見師傅了~
既然是師傅的朋友,就叫你師友大大好了XDD
太實用啦~
謝謝大大的教學,最近我把我的30篇文章都寫完了,就開始來搞這個自動發文...XD
和大家分享我寫的node js自動發文的程式
cron download
在撰寫的過程中,我覺得最困難的就是當有問題時,伺服器總是回答500 error,我很難理解這error到底是error在那邊。
是認證不過,還是.....?
最後我發現我卡住的是在tags
...
因為是多選select
,名稱應為tags[]
而非tags
不過因為難以debug,搞好久~(淚
版大能分享當遇到500 ERROR該從那邊找線索嗎?
另外,你是怎麼知道發文的Content-Type
是application/x-www-form-urlencoded
的呢?
當時卡在500 error時,這也是我懷疑方向之一。
我發了5篇廢文觀察,還是沒辦法從攔截到我發出的廢文的格式。
(因為我用charles攔不到https,用postman模擬時和寫CODE一樣都是500 error,用瀏覽器被自動跳頁資料就不見了)
大大有推薦的攔截的工具嗎?還是我不會使用QQ
基本上,chrome dev tool 就是最好用的工具。你可以從 dev tool 看到成功發送出去的 request 長怎樣,然後先求有再求好,先確定模擬一樣的 request 是可以 work,然後再一步一步刪去多餘的參數
謝謝 Howard 手把手的說明!!
在用 Postman 打 https://ithelp.ithome.com.tw/articles/create?group=tech
的時候,一直拿不到 redirect URL ,後來發現要關閉自動轉址的功能,才會看到 redirect 的 URL ,分享一下:
感謝補充
請問如何用request模擬直接登入iThome會員https://member.ithome.com.tw/login ?
(您上面的例子是先手動登入然後複製cookie來用)
好的,我凌晨來發一篇關於登入的
喔喔喔喔喔喔喔喔太讚啦 我卡關卡這卡5個小時....
終於看到您的分享了
FUCK!
這麼重要的東西要先說! 謝謝分享